/**
 * JAVACC DEMO 1.0
 */
package com.apache.uct.common.filter;

import com.apache.api.manager.ProxyManager;
import com.apache.api.vo.ParamsVo;
import com.apache.database.constant.SpringContextLoader;
import com.apache.database.constant.SystemTools;
import com.apache.passport.common.DesUtils;
import com.apache.passport.common.PassportHelper;
import com.apache.passport.common.XmlWhiteUtils;
import com.apache.passport.entity.Token;
import com.apache.tools.ConfigUtil;
import com.apache.tools.StrUtil;
import com.apache.uct.common.LoginUser;
import com.apache.uct.common.ToolsUtil;
import com.apache.uct.common.entity.Act;
import com.apache.uct.common.entity.Role;
import com.apache.uct.service.plugins.ActCacheHelper;
import org.apache.log4j.Logger;

import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * description:  自定义拦截器,拦截action,do等权限控制的地址(单点与uct集成时使用)
 *
 * @author Hou Dayu 创建时间：2016-2-3
 */
public class SsoServerFilter implements Filter {

    private Logger log = Logger.getLogger(SsoServerFilter.class);

    private static final String SUFFIX = "js,css,png,jpg,gif,bmp,swf,fla,ico";

    private String COOKIENAME = "";

    //自定义登录页面
    private String login_url = "";

    //白名单
    private String white_url = "";

    //访问未授权地址,跳转url
    private String jumpUrl = "";

    // rpc服务地址,引入此属性,主要是因为加密统一
    private String rpc_service_url = "";

    private ProxyManager uctProxyManager;

    private Map<String, String> unLockMap = new HashMap<String, String>();

    private Map<String, ArrayList<String>> whiteMap = new HashMap<String, ArrayList<String>>();

    private String errorPage = "";

    ///监控请求白名单
    private List<String> whiteJK= new ArrayList<String>() {{
        add("/health");
        add("/info");
        add("/auditevents");
        add("/metrics");
        add("/logfile");
        add("/loggers");
        add("/jolokia");
        add("/flyway");
        add("/liquibase");
        add("/dump");
        add("/heapdump");
        add("/auditevents");
        add("/env");
        add("/turbine");
        add("/trace");
        add("/hystrix");
        add("/api");
        add("/refresh");
    }};

    /**
     * TODO 简单描述该方法的实现功能（可选）.
     */
    public void init(FilterConfig arg0) throws ServletException {
        //初始化uct服务端 白名单配置
        unLockMap.put("/common/image.jsp", "");
        unLockMap.put("/uct/service/", "");
        unLockMap.put("/passport/outside", "");
        unLockMap.put("/login.jsp", "");
        unLockMap.put("/mobile/service", "");
        unLockMap.put("/error.action", "");
        //unLockMap.put("/index.action", "");
        //错误页面地址
        errorPage = StrUtil.doNull(arg0.getInitParameter("errorPage"), "/");
        initValue();
        uctProxyManager = (ProxyManager) SpringContextLoader.getBean("uctProxyManager");
    }

    /**
     * TODO 自定义拦截器,服务端不适用white.xml,其只为客户端提供配置服务
     * 过滤url
     * 过滤用户
     * 过滤url后缀
     * 过滤css.js.等
     *
     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        resp.setCharacterEncoding("UTF-8");
        resp.setDateHeader("expires", 0);
        resp.setHeader("Cache-Control", "no-cache");
        resp.setHeader("pragma", "no-cache");
        req.setCharacterEncoding("UTF-8");
        //CookeHeader(resp, req);
        //判断是否为外部系统登录
        String sysEname = request.getParameter("sys");
        String setCookieUrl = request.getParameter("cset");
        String goUrl = request.getParameter("go");
        String clientId = request.getParameter("_client");
        String ptlang = request.getParameter("ptlang");

        //获取uri信息
        String uri = req.getRequestURI();
        //获取访问路径
        String servletPath = req.getServletPath();
        //获取后缀名
        String suffix = PassportHelper.getInstance().parseUrlSuffix(uri);
        //后缀名小写
        suffix = suffix.toLowerCase();

        //如果存在后缀为图片,css等格式,直接跳过,不拦截
        if (StrUtil.isNotNull(suffix)) {
            if (SUFFIX.contains(suffix)) {
                chain.doFilter(req, resp);
                return;
            }
        }
        /**监控白名单处理*/
        if(jkWhite(req.getServletPath())){
            chain.doFilter(req, resp);
            return;
        }
        //获取token
        String tokenCookie = PassportHelper.getInstance().getCurrCookie(req);
        //获取ucsso
        String ucsso = PassportHelper.getInstance().getCurrCookie(req, "_uc.sso");

        if (ConfigUtil.getInstance().checkFileUpdate("")) {
            initValue();
        }
        //2015年2月4日11:07:24 更新 如果客户端根据token访问获取,如果不存在值,则从request中取值
        String pkt = req.getParameter("tokenId");
        if (!StrUtil.isNull(pkt)) {//第三方系统或客户端
            outwardSys(goUrl, pkt, setCookieUrl, ptlang, req, resp);
            return;
        }
        Token token = (Token) SystemTools.getInstance().getCache("loginToken")
                .getCacheCloneByKey(tokenCookie);

        if (ToolsUtil.isEmpty(token)) {//白名单处理
            if (actPathGoto(req)) {
                chain.doFilter(request, response);
                return;
            }
        } else {
            if ((!ToolsUtil.isNull(sysEname) && !ToolsUtil.isNull(setCookieUrl) && !ToolsUtil
                    .isNull(goUrl) && !ToolsUtil.isNull(clientId))) {
                //获取tokenId,然后跳进连入地址
                req.setAttribute("tokenId", token.getTokenId());
                resp.sendRedirect(
                        setCookieUrl + "?ticket=" + token.getTokenId() + "&go=" + goUrl + "&ucsso="
                                + ucsso);
                return;
            } else {
                String desEname = token.getUserEname();
                LoginUser loginUser = initLoginUser(req, resp, tokenCookie, desEname);
                if (actPathGoto(req)) {
                    chain.doFilter(request, response);
                    return;
                }
                String onlyLoginUrl = ToolsUtil.getInstance().getValueByKey("unity_noright_url");
                if (ToolsUtil.isNotNull(onlyLoginUrl)) {
                    String[] wus = onlyLoginUrl.split(",");
                    for (int i = 0; i < wus.length; i++) {
                        String wurl = wus[i];
                        if (ToolsUtil.isNotNull(wurl)) {
                            if (servletPath.startsWith(wurl)) {
                                chain.doFilter(request, response);
                                return;
                            }
                        }
                    }
                }
                String ctxpath = req.getContextPath();
                if ("1".equals(SystemTools.getInstance().getValue("usage_patterns"))) {
                    if (!canAccess(loginUser, servletPath)) {//验证访问权限
                        req.getSession().setAttribute("errorMsg", "对不起,您没有访问权限!!");
                        resp.sendRedirect(ctxpath + errorPage);
                        log.error("没有操作权限");
                        return;
                    }
                }
                chain.doFilter(request, response);
                return;
            }
        }
        //如果不存在.则跳转其他页面,默认为登录页面
        resp.sendRedirect(login_url);
    }

    private boolean actPathGoto(HttpServletRequest request) {
        String servletPath = request.getServletPath();
        Iterator<String> it = unLockMap.keySet().iterator();
        while (it.hasNext()) {
            String url = it.next();
            if (servletPath.indexOf(url) > -1) {
                return true;
            }
        }
        if (white_url.contains(servletPath)) {
            return true;
        }
        //白名单处理
        if (unlockPath(servletPath)) {
            return true;
        }
        return false;
    }

    /**
     * description:  外部系统调用
     */
    private void outwardSys(String goUrl, String pkt, String setCookieUrl, String ptlang,
            HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        Token token = (Token) SystemTools.getInstance().getCache("loginToken")
                .getCacheCloneByKey(pkt);
        //如果为空,则让其跳转到客户端系统进行登录
        if (ToolsUtil.isEmpty(token)) {
            resp.sendRedirect(ToolsUtil.isNull(goUrl) ? login_url : goUrl);
            return;
        }
        String userName = DesUtils.getInstance().decrypt(token.getUserEname());
        //只处理客户端请求,加此判断主要用于passport系统使用tokenId登录时,防止误入此方法
        if ((!ToolsUtil.isNull(pkt) && !ToolsUtil.isNull(goUrl))) {
            //ptlang
            ArrayList<String> lst = new ArrayList<String>();
            lst.add(rpc_service_url);
            lst.add(pkt);
            lst.add(setCookieUrl);
            lst.add(goUrl);
            lst.add("apache");
            Collections.sort(lst);
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < lst.size(); ++i) {
                sb.append((String) lst.get(i));
            }
            String ptlangSha1 = PassportHelper.getInstance().SHA1(sb.toString());
            log.debug("rpc_url msg:[{}]" + rpc_service_url);
            log.debug("pkt msg:[{}]" + pkt);
            log.debug("setCookieUrl msg:[{}]" + setCookieUrl);
            log.debug("goUrl msg:[{}]" + goUrl);
            log.debug("ptlangSha1=" + ptlangSha1 + "; ptlang=" + ptlang);

            if (ptlangSha1.equals(ptlang)) {
                Cookie ticket = new Cookie(COOKIENAME, pkt);
                ticket.setPath("/");
                ticket.setMaxAge(-1);
                resp.addCookie(ticket);

                Cookie ucss = new Cookie("_uc.sso", userName);
                ucss.setPath("/");
                ucss.setMaxAge(-1);
                resp.addCookie(ucss);
                req.setAttribute("tokenId", token.getTokenId());
                resp.sendRedirect(
                        setCookieUrl + "/cset" + "?ticket=" + token.getTokenId() + "&go=" + goUrl
                                + "&ucsso=" + userName);
            } else {
                log.debug("passport msg:[{}]=Keys Are Different");
                req.getRequestDispatcher(jumpUrl).forward(req, resp);
            }
            return;
        } else {
            Cookie ticket = new Cookie(COOKIENAME, pkt);
            ticket.setPath("/");
            ticket.setMaxAge(-1);
            resp.addCookie(ticket);

            Cookie ucss = new Cookie("_uc.sso", userName);
            ucss.setPath("/");
            ucss.setMaxAge(-1);
            resp.addCookie(ucss);
            req.setAttribute("tokenId", token.getTokenId());
        }
    }

    /**
     * description:  获取loginUser信息
     */
    private LoginUser initLoginUser(HttpServletRequest request, HttpServletResponse response,
            String cookieValue, String loginName) {
        LoginUser loginUser = null;
        if (ToolsUtil.isNotNull(cookieValue)) {
            loginUser = (LoginUser) request.getSession().getAttribute("loginUser");//从session中获取用户对象
            if (ToolsUtil.isEmpty(loginUser)) {
                String value = DesUtils.getInstance().decrypt(loginName);
                if (ToolsUtil.isNotNull(value)) {
                    ParamsVo vo = new ParamsVo();
                    vo.setParams("userEname", value);
                    vo.setKey("loginUser");
                    loginUser = (LoginUser) uctProxyManager.doInvoke(vo);
                    if (!ToolsUtil.isEmpty(loginUser)) {
                        request.getSession().setAttribute("loginUser", loginUser);
                        request.getSession().setAttribute("sysUser", loginUser.getSysFlag());
                        if (ToolsUtil
                                .isEmpty(request.getSession().getAttribute("loginUserRoles"))) {
                            List<Role> roles = ActCacheHelper.getInstance()
                                    .getRolesForUserEname(loginUser.getUserEname());//获取用户角色列表
                            if (ToolsUtil.isEmpty(roles)) {
                                request.getSession().setAttribute("loginUserRoles", roles);
                            }
                        }
                    }
                }
            }
        }
        return loginUser;
    }

    /**
     * description:  白名单判断
     */
    private boolean unlockPath(String path) {
        XmlWhiteUtils.getInstance().deWhiteXml(whiteMap, "");
        ArrayList<String> whiteUrl = (ArrayList<String>) whiteMap.get("whiteUrl");
        ArrayList<String> whiteParadigm = (ArrayList<String>) whiteMap.get("whiteParadigm");
        int wp = whiteParadigm.size();
        if (path.length() > 1) {
            if (whiteUrl.contains(path)) {
                return true;
            }
            for (int i = 0; i < wp; i++) {
                if (((String) whiteParadigm.get(i)).contains("*")) {
                    String wdir = ((String) whiteParadigm.get(i)).replace("*", "");
                    int s = path.indexOf(wdir);
                    if (s == 0) {
                        return true;
                    }
                } else if (!"".equals(whiteParadigm.get(i))) {
                    int s = path.indexOf((String) whiteParadigm.get(i));
                    if (s == 0) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * description:  权限验证
     */
    private boolean canAccess(LoginUser loginUser, String path) {
        boolean mark = false;
        if (loginUser.getUserEname().equals(StrUtil
                .doNull(SystemTools.getInstance().getValue("uct_supper_user"), "admin"))) {
            return true;
        }
        Map<String, Act> acts = loginUser.getActMap();
        if (null != acts && !acts.isEmpty()) {
            for (String key : acts.keySet()) {
                Act act = acts.get(key);
                if (StrUtil.isNotNull(act.getActUrl())) {
                    String str[] = act.getActUrl().split(",");
                    for (int i = 0; i < str.length; i++) {
                        if (str[i].endsWith(".action")) {
                            if (path.equals(str[i]))
                                return true;
                        } else if (path.startsWith(str[i])) {
                            return true;
                        }
                    }
                }
            }
        }
        return mark;
    }

    /**
     * TODO 简单描述该方法的实现功能（可选）.
     */
    public void destroy() {
    }

    private void initValue() {
        COOKIENAME = SystemTools.getInstance().getValue("cookieName");
        //自定义登录页面
        login_url = SystemTools.getInstance().getValue("login.url");
        //白名单
        white_url = SystemTools.getInstance().getValue("white.url");
        //访问未授权地址,跳转url
        jumpUrl = SystemTools.getInstance().getValue("jump.url");
        rpc_service_url = SystemTools.getInstance().getValue("rpc_service_url");
    }

    private boolean jkWhite(String servletPath){
        for (int i = 0; i < whiteJK.size(); i++) {
            String wurl = whiteJK.get(i);
            if (servletPath.startsWith(wurl)) {
                return true;
            }
        }
        return false;
    }

}
